package Wangcache

import (
	"Wangcache/consistenthash"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

//我们创建一个结构体HTTPPool，作为承载节点间HTTP通信的核心数据结构（包括服务端和客户端，今天只实现服务端）
//HTTPPool只有两个参数，一个是self，用来记录自己的地址，包括主机名/ip和端口
//另一个是basePath，作为节点间通讯地址的前缀，默认是/_wangcache/，那么http://example.com/_geecache/开头的请求就用于节点间的访问。因为一个主机上可能还承载
//其他服务，加一段Path是一个好习惯。比如，大部分网站的API接口，一般以/api作为前缀

const (
	defaultBasePath="/_wangcache/"
	defaultReplicas=50
)

type HTTPPool struct {
	self string
	basePath string
	mu sync.Mutex
	peers *consistenthash.Map //用来根据具体的key选择节点
	httpGetters map[string]*httpGetter//映射远程节点与对应的httpGetter。每一个远程节点对应一个httpGetter，因为httpGetter与远程节点的地址baseURL有关
}

func NewHTTPPool(self string)*HTTPPool  {
	return &HTTPPool{
		self: self,
		basePath: defaultBasePath,
	}
}

//接下来实现最核心的ServeHTTP方法
func (p *HTTPPool)Log(format string,v ...interface{}){
	log.Printf("[Server %s] %s",p.self,fmt.Sprintf(format,v...))
}
//ServerHTTP handle all http requests
func (p *HTTPPool)ServeHTTP(w http.ResponseWriter,r *http.Request){
	if !strings.HasPrefix(r.URL.Path,p.basePath){//首先判断访问的路径前缀商标是basePath，不是返回错误
		panic("HTTPPool serving unexpected path:"+r.URL.Path)
	}
	p.Log("%s %s",r.Method,r.URL.Path)
	// /<basepath>/<groupname>/<key> required
	parts:=strings.SplitN(r.URL.Path[len(p.basePath):],"/",2)
	if len(parts)!=2{
		http.Error(w,"bad request",http.StatusBadRequest)
		return
	}
	groupName:=parts[0]
	key:=parts[1]

	group:=GetGroup(groupName)
	if group==nil{
		http.Error(w,"no such group"+groupName,http.StatusNotFound)
		return
	}

	view,err:=group.Get(key)
	if err!=nil{
		http.Error(w,err.Error(),http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type","application/octet-stream")
	w.Write(view.ByteSlice())
}

//实现peerPicker接口
//set()方法实例化了一致性哈希算法，并且添加了传入的节点
//并为每一个节点创建了一个HTTP客户端httpGetter
func (p *HTTPPool)Set(peers ...string){
	p.mu.Lock()
	defer p.mu.Unlock()
	p.peers=consistenthash.New(defaultReplicas,nil)
	p.peers.Add(peers...)
	p.httpGetters=make(map[string]*httpGetter,len(peers))
	for _,peer:=range peers{
		p.httpGetters[peer]=&httpGetter{baseURL: peer+p.basePath}
	}
}



//PickerPeer()包装了一致性哈希算法的Get()方法，根据具体的key，选择节点，返回节点对应的HTTP客户端
func (p *HTTPPool)PickPeer(key string)(PeerGetter,bool){
	p.mu.Lock()
	defer p.mu.Unlock()
	if peer:=p.peers.Get(key);peer!=""&&peer!=p.self{
		p.Log("pick peer %s",peer)
		return p.httpGetters[peer],true
	}
	return nil,false
}

//为HTTPPool实现客户端功能
//首先创建具体的HTTP客户端类，实现PeerGetter接口
type httpGetter struct {
	baseURL string//你将要访问的远程节点的地址，例如http://example.com/_wangcache/
}

//使用http.Get()方式获取返回值，并转换为[]bytes类型
func (h *httpGetter)Get(group string,key string)([]byte,error){
	u:=fmt.Sprintf(
		"%v%v/%v",
		h.baseURL,
		url.QueryEscape(group),
		url.QueryEscape(key),
		)
	res,err:=http.Get(u)
	if err!=nil{
		return nil,err
	}
	if res.StatusCode!=http.StatusOK{
		return nil,fmt.Errorf("server returned %v",res.Status)
	}
	bytes,err:=ioutil.ReadAll(res.Body)
	if err!=nil {
		return nil, fmt.Errorf("reading response body :%v", err)
	}
	return bytes,nil
}

var _ PeerGetter=(*httpGetter)(nil)

















